use crate::ProtoId::ProtoID;
use warp::Rejection;
use crate::controller::Context;
use crate::{PROTO_SENDER, protos, rspMap, notify};
use crate::model::state_code::StateCode;
use prost::{DecodeError, Message};
use warp::reply::Json;
macro_rules! wait_rsp {
    ($protoId:expr,$serializeNO:expr,$a:ty)=>{
       async {
            loop {
                notify.notified().await;
                let rsp_map_read = rspMap.read().unwrap();

                if let Some(bx) = rsp_map_read.get(&($serializeNO)) {
                    if bx.header.nProtoID == $protoId as i32 {
                        let decoded:Result<$a, prost::DecodeError> = prost::Message::decode(&bx.body_buffer[..]);
                        let result = match decoded {
                            Ok(v) => {
                                Ok(Box::new(v))
                            }
                            Err(e) => {
                                log_util::local_error!("analyse_body_buffer from srv err");
                                Err(e)
                            }
                        };
                        return result;
                    }
                }
            }
        }
    }
}

macro_rules! future_time_out {
    ($ctx:expr, $protoId:expr,$serializeNO:expr,$a:ty)=>{
        async {
            if let Ok(result) =tokio::time::timeout(std::time::Duration::from_secs(10), wait_rsp!( $protoId, $serializeNO, $a)).await {
                match result {
                    Ok(v) => $ctx.response(StateCode::Success.into(), Some(&v)),
                    Err(e) => $ctx.response(StateCode::Error.into(), Some(&format!("deserialize response failed:{}", e.to_string()))),
                }
            } else {
                 $ctx.response(StateCode::RequestTimeout.into(), Option::<&i32>::None)
            }
        }
    }
}

pub async fn request_to_srv<T: prost::Message>(ctx: Context, protoId: ProtoID, request: &T) -> Result<impl warp::Reply, Rejection> {
    let proto_sender = PROTO_SENDER.get().unwrap();
    let header = proto_sender.sendProto(protoId, request).await.expect(&format!("sendProto failed:{:?}", protoId));
    let rr = sync_rsp(ctx, protoId, header.nSerialNo).await;
    remove_elem_in_rsp_map(header.nSerialNo);
    rr
}

fn remove_elem_in_rsp_map(serializeNO: i32) {
    let mut rsp_map_write = rspMap.write().unwrap();
    rsp_map_write.remove(&serializeNO);
}

async fn sync_rsp(ctx: Context, protoId: ProtoID, serializeNO: i32) -> Result<Json, Rejection> {
    match protoId {
        ProtoID::QOT_GETKL => future_time_out!(ctx, protoId, serializeNO, protos::qot_get_kl::Response).await,
        ProtoID::QOT_SUB => future_time_out!(ctx, protoId, serializeNO, protos::qot_sub::Response).await,
        ProtoID::QOT_REGQOTPUSH => future_time_out!(ctx, protoId, serializeNO, protos::qot_reg_qot_push::Response).await,
        _ => ctx.response(StateCode::ParamError.into(), Some(&format!("protoId不存在:{}", protoId as i32))),
    }
}
